Skip to content

Replace text aligner with jsdiff - #533

Merged
max-sixty merged 1 commit into
mainfrom
codex/jsdiff-text-alignment
Sep 10, 2026
Merged

Replace text aligner with jsdiff#533
max-sixty merged 1 commit into
mainfrom
codex/jsdiff-text-alignment

Conversation

@max-sixty

@max-sixty max-sixty commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Leaf's core browser runtime carried a complete Hirschberg LCS implementation even though the optional Pierre bundle already established jsdiff as a compatible dependency. This replaces the handwritten aligner with a pinned, tree-shaken jsdiff 9.0.0 diffArrays bundle while keeping Leaf's word/sentence segmentation, common-edge fallback, and 60% inline-refinement policy local.

The 3,024-byte bundle rebuilds byte-for-byte. Existing fixture outputs matched exactly across word, sentence, and inline alignment, including repeated-word cases.

The static dependency adds one JavaScript request before presentation. It stays eager because alignText is a synchronous widget API and authored suggestions compute their word emphasis during upgrade; deferring the dependency would make that meaning arrive only after the page had presented.

Tests: uv run pytest tests/test_render_drafts.py -q -n0 (81 passed); uv run pytest tests (998 passed); pre-commit hooks passed.

This was written by Codex on behalf of @max-sixty

@leaf-agent leaf-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static import { diffArrays } from "/vendor/jsdiff.esm.js" puts a vendor bundle in the eager boot graph for the first time. text-alignment.js is reached eagerly twice over — leaf.jsruntime/version.js, and leaf.jscomposing/selection.jscomposing/capture.js for textUnits — and the published site can't fold the bundle away, because worker/bundle-runtime.mjs's preserve-module-urls resolver keeps every /vendor/ path external. So every page fetches jsdiff before presentation whether or not the reader ever opens a draft history or a version comparison. The two ci runs measure it:

page main this PR
/ 11 JS / 207 KiB 12 JS / 208 KiB
/examples/triage-board/ 10 JS / 181 KiB 11 JS / 182 KiB
/examples/feature-gallery/versions/v1.html 36 JS / 809 KiB 37 JS / 810 KiB

The bytes are noise; the request is the part CLAUDE.md asks about. The layer's other two core-runtime vendor bundles are both deferred behind first use — runtime/syntax.js's hljsReady ??= import("/vendor/highlight.esm.js") and runtime/markdown.js's ready ??= import("/vendor/marked.esm.js") — and Working on the repository asks a change that adds work before presentation to "state the user-visible benefit and why that work cannot wait until after presentation", which the description doesn't.

There's no mechanical fix to suggest: deferring the import makes alignText async, and it's a public runtime/widget-api.js export, so the trade is one eager request per page against an async alignment surface. That's your call rather than mine — either resolution closes this, whether that's naming why the fetch belongs at startup or moving it behind first use.

@max-sixty
max-sixty merged commit 5e0825a into main Sep 10, 2026
7 checks passed
@max-sixty
max-sixty deleted the codex/jsdiff-text-alignment branch September 10, 2026 05:33
max-sixty pushed a commit that referenced this pull request Sep 10, 2026
…ule (#543)

`scripts/CLAUDE.md` tells a session that a clean `git status` after
`scripts/vendor.py <bundle>` is the check that a bundle still matches
the script, and that the check holds only where every fetched input is
pinned. It then sorts the bundles into those two classes — and two of
the seven are in neither, so a session rebuilding `floating-ui` or
`mcp-app` has no answer for whether a diff it sees is drift to fix or an
upstream patch to take.

Each belongs in a class the paragraph already describes:

- `floating-ui` reproduces. `@floating-ui/dom`, `@floating-ui/core`, and
`@floating-ui/utils` are all in `PINS`, and that closure is complete:
`dom` depends on `core` and `utils`, `core` on `utils`, `utils` on
nothing. Rebuilding it left `git status` clean.
- `mcp-app` fetches an input `PINS` does not name.
`@modelcontextprotocol/ext-apps@1.7.5` declares `@standard-schema/spec
^1.1.0`, which npm's resolver picks, so the bundle sits with `plot` and
`pierre`. It reproduced when I rebuilt it today — which is exactly the
reading the paragraph exists to keep a session from relying on.

This is drift rather than a deliberate omission. The paragraph dates
from `c7b37062` (2026-08-30); `mcp-app` landed in #188 on 2026-09-01 and
`floating-ui` in #537 last night, and #533 — the most recent commit to
touch the paragraph — added `jsdiff` to it, so it is meant to be kept
current.

The test follows the shape this repo already uses for instruction lists
(#93, #105, #331, #496): it reads the bundle names out of `vendor.py`'s
`BUILDS` and `COPIES` rather than restating them, and asserts each is
named in that one paragraph. On `main` it fails with `unplaced in
scripts/CLAUDE.md: ['floating-ui', 'mcp-app']`.

What it deliberately does not check is *which* class a bundle is placed
in — deciding that means resolving each package's dependency graph,
which is a network call this suite should not make. A name in the wrong
class still needs a reader to catch; a name that is absent no longer
does.

<details><summary>Verification</summary>

- `uv run pytest tests/test_interact_layer.py` — 167 passed, 6 skipped.
- The new case alone, with `scripts/CLAUDE.md` reverted to `main`'s
text: `AssertionError: unplaced in scripts/CLAUDE.md: ['floating-ui',
'mcp-app']`.
- `scripts/vendor.py floating-ui` and `scripts/vendor.py mcp-app` each
left `git status` clean.
- `npm view @floating-ui/dom@1.8.0 dependencies` → `{
'@floating-ui/core': '^1.8.0', '@floating-ui/utils': '^0.2.12' }`;
`@floating-ui/core@1.8.0` → `{ '@floating-ui/utils': '^0.2.12' }`;
`@floating-ui/utils@0.2.12` → none;
`@modelcontextprotocol/ext-apps@1.7.5` → `{ '@standard-schema/spec':
'^1.1.0' }`.

</details>

Co-authored-by: leaf-agent <318509791+leaf-agent@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants